unit Conv;

{
  PasCalc
  Version 1.2 2011-05-26

  Copyright (C) 2011 KEEPER <yar.keeper@gmail.com>

  This file is part of the PasCalc Calculator

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  See the file LICENSE.TXT, included in this distribution,
  for details about the copyright.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
}

{$mode Delphi}

interface

uses
  Classes, SysUtils;

type

  KOLString = ANSIString;

  TCoefType = (ctDiv, ctMult, ctAdd, ctMinus);

  TConversion = packed record
    Name: ShortString;
    Coef: extended;
    CoefType: TCoefType;
  end;

  TRoute = class(TList)
  private
    fRouteName: ShortString;
    procedure GetRouteName(const Value: ShortString);
    function Get(Index: Integer): TConversion;
    procedure Put(Index: Integer; const Value: TConversion);
    procedure DispElt(const Index: integer);
  public
    destructor Destroy; override;
    function Add(const Item: TConversion): Integer;
    property RouteName: ShortString read fRouteName write GetRouteName;
    property Items[Index: Integer]: TConversion read Get write Put; default;
  end;

  TGroup = Class(TList)
  private
    fGroupName: ShortString;
    function Get(Index: Integer): TRoute;
    procedure Put(Index: Integer; const Value: TRoute);
    procedure SetGroupName(const Value: ShortString);
  public
    destructor Destroy; override;
    property Items[Index: Integer]: TRoute read Get write Put; default;
    property GroupName: ShortString read fGroupName write SetGroupName;
    function Add(const Item: TRoute): Integer;
  end;

  TConverter = class
  private
    fConvsList: TList;
    function GetConvsCount(GroupIdx, RouteIdx: integer): integer;
    function GetGroupCount: integer;
    function GetRoutesCount(GroupIdx: integer): integer;
    function GetConversion(GroupIdx, RouteIdx, ConvIdx: integer): TConversion;
    function GetGroupName(GroupIdx: integer): ShortString;
    function GetRouteName(GroupIdx, RouteIdx: integer): ShortString;
  public
    constructor Create;
    destructor Destroy; override;
    procedure Add(const GroupName, RouteName, ConvName: ShortString; const Coef:
      extended; const CoefType: TCoefType);
    procedure SaveToFile(const Name: KOLString);
    procedure LoadFromFile(const Name: KOLString);
    property GroupsCount: integer read GetGroupCount;
    property RoutesCount[GroupIdx: integer]: integer read GetRoutesCount;
    property ConvsCount[GroupIdx, RouteIdx: integer]: integer read GetConvsCount;
    property GroupName[GroupIdx: integer]: ShortString read GetGroupName;
    property RouteName[GroupIdx, RouteIdx: integer]: ShortString
      read GetRouteName;
    property Conversion[GroupIdx, RouteIdx, ConvIdx: integer]: TConversion
      read GetConversion;
  end;

implementation

{ TRoute }

function TRoute.Add(const Item: TConversion): Integer;
var
  Conv: ^TConversion;
begin
  New(Conv);
  Conv^ := Item;
  result := inherited Add(Conv);
end;

destructor TRoute.Destroy;
var
  i: integer;
begin
  for i := 0 to Self.Count - 1 do
    DispElt(i);
  inherited Destroy;
end;

procedure TRoute.DispElt(const Index: integer);
type
  PConversion = ^TConversion;
begin
  Dispose(PConversion(inherited Items[index]));
end;

function TRoute.Get(Index: Integer): TConversion;
type
  PConversion = ^TConversion;
begin
  result := PConversion(inherited Get(Index))^
end;

procedure TRoute.GetRouteName(const Value: ShortString);
begin
  fRouteName := Value;
end;

procedure TRoute.Put(Index: Integer; const Value: TConversion);
var
  Conversion: ^TConversion;
begin
  New(Conversion);
  Conversion^ := Value;
  inherited Put(Index, Conversion)
end;

{ TGroup }

function TGroup.Add(const Item: TRoute): Integer;
begin
  result := inherited Add(Item)
end;

destructor TGroup.Destroy;
var
  i: integer;
begin
  for i := 0 to Count - 1 do
    items[i].Free;
  inherited Destroy;
end;

function TGroup.Get(Index: Integer): TRoute;
begin
  Result := TRoute(inherited Get(index));
end;

procedure TGroup.Put(Index: Integer; const Value: TRoute);
begin
  inherited Put(Index, Value);
end;

procedure TGroup.SetGroupName(const Value: ShortString);
begin
  fGroupName := Value;
end;

{ TConverter }

procedure TConverter.Add(const GroupName, RouteName, ConvName: ShortString;
  const Coef: extended; const CoefType: TCoefType);
var
  Group: TGroup;
  Route: TRoute;
  Conv: TConversion;
var
  i, j: integer;
begin
  Conv.Name := ConvName;
  Conv.Coef := Coef;
  Conv.CoefType := CoefType;
  for i := 0 to fConvsList.Count - 1 do
    if TGroup(fConvsList[i]).fGroupName = GroupName then
    begin
      Group := TGroup(fConvsList[i]);
      for j := 0 to Group.Count - 1 do
        if Group[j].fRouteName = RouteName then
        begin
          Group[j].Add(Conv);
          exit;
        end;
      Route := TRoute.Create;
      Route.RouteName := RouteName;
      Route.Add(Conv);
      Group.Add(Route);
      exit;
    end;
  group := TGroup.Create;
  Group.GroupName := GroupName;
  Route := TRoute.Create;
  Route.RouteName := RouteName;
  Route.Add(Conv);
  Group.Add(Route);
  fConvsList.Add(Group)
end;

constructor TConverter.Create;
begin
  fConvsList := TList.Create;
end;

destructor TConverter.Destroy;
var
  i: integer;
begin
  for i := 0 to fConvsList.Count - 1 do
    TGroup(fConvsList.Items[i]).Free;
  fConvsList.Free;
  inherited Destroy;
end;

function TConverter.GetConversion(GroupIdx, RouteIdx,
  ConvIdx: integer): TConversion;
begin
  result := TGroup(fConvsList[GroupIdx])[RouteIdx][ConvIdx]
end;

function TConverter.GetConvsCount(GroupIdx, RouteIdx: integer): integer;
begin
  result := TGroup(fConvsList[GroupIdx])[RouteIdx].Count;
end;

function TConverter.GetGroupCount: integer;
begin
  result := fConvsList.Count;
end;

function TConverter.GetGroupName(GroupIdx: integer): ShortString;
begin
  result := TGroup(fConvsList[GroupIdx]).fGroupName;
end;

function TConverter.GetRouteName(GroupIdx, RouteIdx: integer): ShortString;
begin
  result := TGroup(fConvsList[GroupIdx])[RouteIdx].fRouteName;
end;

function TConverter.GetRoutesCount(GroupIdx: integer): integer;
begin
  result := TGroup(fConvsList[GroupIdx]).Count;
end;

procedure TConverter.LoadFromFile(const Name: KOLString);
var
  GroupCount, RouteCount, ConvCount: integer;
  Group: TGroup;
  Route: TRoute;
  Conv: TConversion;
  Stream: TFileStream;
  i, j, k: integer;
begin
  Stream := TFileStream.Create(Name, fmOpenRead);
  try
    Stream.Read(GroupCount, SizeOf(DWORD));
    for I := 0 to GroupCount - 1 do
    begin
      Group := TGroup.Create;
      Stream.Read(Group.fGroupName, SizeOf(ShortString));
      Stream.Read(RouteCount, SizeOf(DWORD));
      for j := 0 to RouteCount - 1 do
      begin
        Route := TRoute.Create;
        Stream.Read(Route.fRouteName, SizeOf(ShortString));
        Stream.Read(ConvCount, SizeOf(DWORD));
        for k := 0 to ConvCount - 1 do
        begin
          Stream.Read(Conv, SizeOf(TConversion));
          Route.Add(Conv)
        end;
        Group.Add(Route)
      end;
      fConvsList.Add(Group);
    end;
  finally
    Stream.Free;
  end;
end;

procedure TConverter.SaveToFile(const Name: KOLString);
var
  GroupCount, RouteCount, ConvCount: integer;
  Group: TGroup;
  Route: TRoute;
  Conv: TConversion;
  Stream: TFileStream;
  i, j, k: integer;
begin
  GroupCount := fConvsList.Count;
  Stream := TFileStream.Create(Name, fmCreate);
  try
    Stream.Write(GroupCount, SizeOf(DWORD));
    for I := 0 to GroupCount - 1 do
    begin
      Group := TGroup(fConvsList[i]);
      Stream.Write(Group.fGroupName, SizeOf(ShortString));
      RouteCount := Group.Count;
      Stream.Write(RouteCount, SizeOf(DWORD));
      for j := 0 to RouteCount - 1 do
      begin
        Route := Group[j];
        Stream.Write(Route.fRouteName, SizeOf(ShortString));
        ConvCount := Route.Count;
        Stream.Write(ConvCount, SizeOf(DWORD));
        for k := 0 to ConvCount - 1 do
        begin
          Conv := Route[k];
          Stream.Write(Conv, SizeOf(TConversion));
        end;
      end;
    end;
  finally
    Stream.Free;
  end;
end;

end.

